package rss_GUI;

import java.awt.Toolkit;
import java.awt.Component;
import java.text.SimpleDateFormat;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.swing.RowSorter.SortKey;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import rss_CODE.RadioStationSystem;
import rss_CODE.ExportImport;
import rss_POJO.*;

/**
 *
 * @author RS-team
 */
public class PlayListManagement extends javax.swing.JFrame {

    private static final String datetimeRenderer = "EEE d MMM y, hh:mm:ss aa";  // πρότυπο ημερομηνίας & ώρας για εμφάνιση ή έλεγχο στις φόρμες
    private static final Integer minimumDuration = 1800;  // η απαίτηση για ελάχιστη συνολική διάρκεια 30' (σε sec) Τραγουδιών στην Λίστα Αναπαραγωγής

    private final RadioStationSystem rss;
    private final EntityManager em;
    private final MainMenu mm;
    private ExportImport ei;

    private AvailableSongsTable ast;

    private final String frameTitle;

    private Boolean addORedit;  // false: κλήση για EDIT, true: κλήση για ADD
    private Boolean editStatus;  // βρισκόμαστε σε κατάσταση editing των πεδίων της φόρμας ή όχι
    private Integer fieldFocused;  // ποιο πεδίο της φόρμας είναι focused
    private String tempTxt;  // προσωρινή μεταβλητή String πεδίων
    private final PlayList tempPL;  // προσωρινά αποθηκευμένη PlayList
    private PlayList pl;
 

    /**
     * Creates new form PlayListManagement
     */
    public PlayListManagement(MainMenu mm) {
        this.rss = mm.getRSS();
        this.em = mm.getEM();
        this.mm = mm;
        this.addORedit = null;
        this.editStatus = false;
        this.fieldFocused = 0;
        this.pl = new PlayList();
        this.tempPL = new PlayList();
        this.tempTxt = "";
        initComponents();
        this.frameTitle = this.getTitle();
        ei = new ExportImport(rss);  // δημιουργία αντικειμένου ei τύπου ExportImport για χρήση μεθόδων του

        jScrollPaneFORM.setVerticalScrollBarPolicy(jScrollPaneFORM.VERTICAL_SCROLLBAR_ALWAYS);
        // αρχικοποίηση και καθαρισμός πεδίων φόρμας επεξεργασίας
        EnableDisableFields(false); ClearAllFields();
    }

    public List<Song> getListSongsInPlayList() {
        return listSongsInPlayList;
    }
    public void setListSongsInPlayList(List<Song> listSongsInPlayList) {
        this.listSongsInPlayList = listSongsInPlayList;
    }

    /* dynamic jpql query που αναζητά εγγραφή στο table PlayList βάσει δοθέντος πεδίου και τιμής πεδίου */
    private Boolean searchPlayList(String field, Object value) {
        try {
            if(addORedit) {
                // σε ADD mode, το αντικείμενο PlayList pl είναι καινούριο οπότε ελεγχω όλη τη ΒΔ
                em.createQuery("SELECT pl FROM PlayList pl WHERE pl." + field + "=:value", PlayList.class)
                        .setParameter("value", value).getSingleResult();
            } else {
                // σε EDIT mode, πρέπει η αναζήτηση να εξαιρεί το ίδιο αντικείμενο PlayList pl μέσω του primary key
                em.createQuery("SELECT pl FROM PlayList pl WHERE pl." + field + "=:value AND pl.playlistid!=:id", PlayList.class)
                        .setParameter("value", value).setParameter("id", pl.getPlaylistid()).getSingleResult();
            }
            return true;  // η αναζήτηση ήταν επιτυχής αφού δεν συνέβει κάποιο σφάλμα
        } catch(NoResultException nrex) {
            // δεν βρέθηκε τίποτα, άρα η εγγραφή είναι μοναδική
            return false;
        } catch(Exception ex) {
            // γενικότερο σφάλμα σύνθεσης ή εκτέλεσης query αναζήτησης PlayList
            rss.criticalErrorBox(this, "σφάλμα query αναζήτησης PLAYLIST " + field);
            // αγνοώντας οποιαδήποτε πιθανά νέα Exceptions προκύψουν,...
            try {
                em.close();  // οριστικό κλείσιμο EntityManager em
            } catch (Exception e) {
                // τίποτα πλέον, έτσι κι αλλιώς πάμε για κλείσιμο
            } finally {
                System.exit(4);  // έξοδος με errorlevel 4
                return null;  // άχρηστο αλλά αναγκαίο
            }
        } 
    }

    /* μέθοδος για πλήρη καθαρισμό των πεδίων και προετοιμασία της playlist τύπου PlayList για GarbageCollector */
    private void clearPlayList(PlayList playlist) {
        // clear όλα τα πεδία
        playlist.setName(null);
        playlist.setCreationdate(null);
        // clear όλες τις λίστες του αντικειμένου
        if(playlist.getSongCollection() != null) playlist.getSongCollection().clear(); playlist.setSongCollection(null);
        playlist.setPlaylistid(null);  // και τέλος το primary key
    }
    
    /* μέθοδος για καθαρισμό όλων των πεδίων της φόρμας επεξεργασίας */
    private void ClearAllFields() {
        txtName.setText("");
        txtCreationDate.setText("");
        txtDuration.setText("");
        if(listSongsInPlayList == null)  // αν η λίστα Τραγουδιών που συνδέεται με τον πίνακα είναι null, δημιούργησε νέα empty
            listSongsInPlayList = org.jdesktop.observablecollections.ObservableCollections.observableList(new ArrayList<Song>());
        else listSongsInPlayList.clear();  // αλλιώς απλά μηδένισε την ήδη υπάρχουσα
    }

    /* μέθοδος για αντιγραφή πεδίων σε PlayList toPL από PlayList fromPL */
    private void CopyAllFields(PlayList toPL, PlayList fromPL) {
        toPL.setName(fromPL.getName());
        toPL.setCreationdate(fromPL.getCreationdate());
        if(fromPL.getSongCollection() != null) {  // αν η πηγαία λίστα Τραγουδιών της fromPL δεν είναι null
            if(toPL.getSongCollection() == null)  // αν η λίστα προορισμού Τραγουδιών της toPL είναι null, δημιούργησε νέα empty list
                toPL.setSongCollection(org.jdesktop.observablecollections.ObservableCollections.observableList(new ArrayList<Song>()));
            toPL.getSongCollection().clear(); toPL.getSongCollection().addAll(fromPL.getSongCollection());  // καθάρισε και κάνε την αντιγραφή
        } else toPL.setSongCollection(null);  // αλλιώς θέσε null και την λίστα προορισμού της toPL
        toPL.setPlaylistid(fromPL.getPlaylistid());  // και τέλος το primary key
    }
    
    /* μέθοδος για ενημέρωση πεδίων φόρμας με τις τιμές του επιλεγμένου αντικειμενου pl τύπου PlayList */
    private void UpdateAllFields() {
        txtName.setText(pl.getName() == null? "" : pl.getName());
        txtCreationDate.setText(pl.getCreationdate() == null? "" : new SimpleDateFormat(datetimeRenderer).format(pl.getCreationdate()));  // επιπλέον μορφοποίησε ημερομηνία
        if(listSongsInPlayList == null)  // αν η λίστα Τραγουδιών που είναι συνδεδεμένη με τον πίνακα είναι null, δημιούργησε νέα empty list
            listSongsInPlayList = org.jdesktop.observablecollections.ObservableCollections.observableList(new ArrayList<Song>());
        else listSongsInPlayList.clear();  // αλλιώς μηδένισε πρώτα την ήδη υπάρχουσα και αν η λίστα Τραγουδών της pl δεν είναι κενή
        if(pl.getSongCollection() != null) listSongsInPlayList.addAll(pl.getSongCollection()); // κάνε την αντιγραφή
        if(listSongsInPlayList.isEmpty()) txtDuration.setText("");  // τίποτα αν η επιλεγμένη Λίστα Τραγουδιών είναι κενή, αλλιώς
        else { int totalDuration = 0; for(Song s : listSongsInPlayList) totalDuration += s.getDuration(); txtDuration.setText(rss.integerToTimeDuration(totalDuration) + " "); }  // υπολογισμός συνολικής διάρκειας Τραγουδιών της Λίστας
    }
    
    /* σύγκριση πεδίων φόρμας επεξεργασίας με το επιλεγμένο αντικειμενο pl τύπου PlayList */
    private Boolean compareAllFields() {
        // σύγκρινε ένα ένα τα πεδία για διαφορές με τα αντίστοιχα της επιλεγμένης PlayList pl προσέχοντας πάντα τα nulls
        if(pl.getName() == null? !txtName.getText().isEmpty() : !txtName.getText().equals(pl.getName())) return false;
        if(pl.getSongCollection() == null? !(listSongsInPlayList == null || listSongsInPlayList.isEmpty()) : (listSongsInPlayList != null? !pl.getSongCollection().equals(listSongsInPlayList) : true)) return false;  // αν οι λίστες δεν είναι κενές, σύγκρινέ τες
        return true;  // όλα τα πεδία ταυτίζονται
    }

    /* πραγματοποιεί ελέγχους σε όλα τα πεδία με πρόβλεψη αλλαγής nullable ή uniqueness κατάστασης από τα POJOs */
    private Boolean validateAllFields() {
        // έλεγχοι για το Name
        if(!txtName.getText().equals(txtName.getText().replace("\\",""))) {  // έλεγχος για ανεπιθύμητους \ χαρακτήρες
            if(!mm.getExpertMode()) { txtName.grabFocus(); rss.infoBox(this, "Δεν επιτρέπονται  \\  σε αλφαριθμητικά"); return false; }  // αν όχι σε ExpertMode
            else { pl.setName(txtName.getText().replace("\\","").trim()); }  // αλλιώς σιωπηλά αφαίρεσε ανεπιθύμητους \ χαρακτήρες και κενά κατέρωθεν του αλφαριθμητικού
        } else pl.setName(txtName.getText().trim());  // αφαίρεσε μόνο τα κενά εκατέρωθεν του αλφαριθμητικού
        if(pl.getName().isEmpty()) pl.setName(null);  // αν είναι κενό μηδένισέ το σε null
        if(pl.getName() != null && pl.getName().length() > rss.fieldLength(this, PlayList.class, "name")) {  // περιορισμός αλφαριθμητικού στο μήκος που δηλώνεται στο POJO
            pl.setName(pl.getName().substring(0, rss.fieldLength(this, PlayList.class, "name"))); }
        if(pl.getName() != null && rss.fieldUnique(this, PlayList.class, "name") && searchPlayList("name", pl.getName())) {  // δυνητικός έλεγχος μοναδικότητας με query
            txtName.grabFocus(); rss.warningBox(this, "το ΟΝΟΜΑ ΛΙΣΤΑΣ ΤΡΑΓΟΥΔΙΩΝ που εισάγατε υπάρχει ήδη    "); return false; }
        if(pl.getName() == null && !rss.fieldNullable(this, PlayList.class, "name")) {  // δυνητικός έλεγχος nullability
            txtName.grabFocus(); rss.warningBox(this, "το ΟΝΟΜΑ ΛΙΣΤΑΣ ΤΡΑΓΟΥΔΙΩΝ δεν μπορεί να είναι κενό    "); return false; }
        // έλεγχοι για το CreationDate
        pl.setCreationdate(new Date());  // για τη δημιουργία ή μεταβολή πάρε απλά την τρέχουσα ημερομηνία και ώρα
        if(pl.getCreationdate() != null && rss.fieldUnique(this, PlayList.class, "creationdate") && searchPlayList("creationdate", pl.getCreationdate())) {  // δυνητικός έλεγχος μοναδικότητας με query
            txtCreationDate.grabFocus(); rss.warningBox(this, "η ΗΜΕΡΟΜΗΝΙΑ πρέπει να είναι μοναδική  "); return false; }
        if(pl.getCreationdate() == null && !rss.fieldNullable(this, PlayList.class, "creationdate")) {  // δυνητικός έλεγχος nullability
            txtCreationDate.grabFocus(); rss.warningBox(this, "η ΗΜΕΡΟΜΗΝΙΑ δεν μπορεί να είναι κενή  "); return false; }
        // έλεγχος ελάχιστης συνολικής διάρκειας (σε sec) Τραγουδιών
        if(listSongsInPlayList == null) { jPanelFORM.grabFocus(); rss.warningBox(this, "η ΛΙΣΤΑ ΤΡΑΓΟΥΔΙΩΝ είναι κενή  "); return false; }
        int totalDuration = 0; for(Song s : listSongsInPlayList) totalDuration += s.getDuration();  // υπολογισμός συνολικής διάρκειας Τραγουδιών της Λίστας
        if(totalDuration < minimumDuration) {  // απαίτηση για τουλάχιστον minimumDuration διάρκεια Τραγουδιών σε κάθε Λίστα
            jPanelFORM.grabFocus(); rss.warningBox(this, "η ελάχιστη συνολική διάρκεια ΤΡΑΓΟΥΔΙΏΝ είναι " + minimumDuration + "sec   "); return false;
        } else {
            if(pl.getSongCollection() == null)  // αν η λίστα Τραγουδιών της pl είναι null, δημιούργησε νέα empty list
                pl.setSongCollection(org.jdesktop.observablecollections.ObservableCollections.observableList(new ArrayList<Song>()));
            else pl.getSongCollection().clear();  // αλλιώς μηδένισε πρώτα την ήδη υπάρχουσα και
            pl.getSongCollection().addAll(listSongsInPlayList); // κάνε την αντιγραφή
        }
        return true; // επιτέλους, όλα ΟΚ!
    }
    
    /* ενεργοποίηση/απενεργοποίηση επιθυμητών components */
    // buttons και components εκτός από jTable και jPanel εκ κατασκευής non-focusable, επιλεκτικό components enabling και focusing
    private void EnableDisableFields(Boolean flag) {
        // για τον πίνακα Τραγουδιών και σχετικών κουμπιών
        //jScrollPaneMAINTABLE.getVerticalScrollBar().setEnabled(!flag);
        jScrollPaneMAINTABLE.setEnabled(!flag); jLabelMAINTABLE.setEnabled(!flag);
        jButtonADD.setEnabled(!flag); jButtonEDIT.setEnabled(!flag); jButtonREMOVE.setEnabled(!flag);
        jButtonIMPORT.setEnabled(!flag); jButtonEXPORT.setEnabled(!flag);
        PlayListTABLE.setEnabled(!flag); PlayListTABLE.getTableHeader().setEnabled(!flag);
        PlayListTABLE.setFocusable(!flag);  // reset/set focus στο jTable
        // για τα πεδία επεξεργασίας Λίστας Τραγουδιών, πίνακα Τραγουδιών και σχετικών κουμπιών
        //jScrollPaneFORM.getVerticalScrollBar().setEnabled(flag);
        jScrollPaneFORM.setEnabled(flag); SongsTABLE.setEnabled(flag);
        jButtonSAVE.setEnabled(flag); jButtonCANCEL.setEnabled(flag);
        for (Component c : jPanelFORM.getComponents()) c.setEnabled(flag);
        jLabelFORM.setEnabled(flag); jPanelFORM.setEnabled(flag);  // τελευταίο το jPanel
        jPanelFORM.setFocusable(flag);  // set/reset focus στο jPanel
        jLabelNOTES.setEnabled(flag); jLabelNANOPLAYER.setEnabled(flag);  // άρρεν ή θήλυ
        // επιπλέον στοιχεία ή κουμπιά προσθήκης/αφαίρεσης μέλους, αρχικά πάντα απενεργοποιημένα
        SongsTABLE.setFocusable(false); jButtonREMOVESONG.setEnabled(false);
        fieldFocused = 0; editStatus = flag;  // set/reset κατάσταση επεξεργασίας
    }
    
    
    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        bindingGroup = new org.jdesktop.beansbinding.BindingGroup();

        try {
            queryPlayList = java.beans.Beans.isDesignTime() ? null : em.createQuery("SELECT pl FROM PlayList pl");
            listPlayList = java.beans.Beans.isDesignTime() ? java.util.Collections.emptyList() : org.jdesktop.observablecollections.ObservableCollections.observableList(queryPlayList.getResultList());
        } catch(Exception ex) {
            rss.warningBox(this, "σφάλμα κατά την ανάγνωση PLAYLISTs");
            // αν υπάρχει προηγούμενη την μηδενίζω και την στέλνω στον GC, κατόπιν δημιουργώ και επιστρέφω νέα κενή λίστα λόγω γενικότερου σφάλματος
            if(listPlayList != null) listPlayList.clear(); listPlayList = null; listPlayList = org.jdesktop.observablecollections.ObservableCollections.observableList(new ArrayList<PlayList>());
        }
        try {
            querySongsInPlayList = java.beans.Beans.isDesignTime() ? null : em.createQuery("SELECT s FROM Song s WHERE s IS NULL");
            listSongsInPlayList = java.beans.Beans.isDesignTime() ? java.util.Collections.emptyList() : org.jdesktop.observablecollections.ObservableCollections.observableList(querySongsInPlayList.getResultList());
        } catch(Exception ex) {
            rss.warningBox(this, "σφάλμα κατά την ανάγνωση SONGs");
            // αν υπάρχει προηγούμενη την μηδενίζω και την στέλνω στον GC, κατόπιν δημιουργώ και επιστρέφω νέα κενή λίστα λόγω γενικότερου σφάλματος
            if(listSongsInPlayList != null) listSongsInPlayList.clear(); listSongsInPlayList = null; listSongsInPlayList = org.jdesktop.observablecollections.ObservableCollections.observableList(new ArrayList<Song>());
        }
        jLabelRSTEAM = new javax.swing.JLabel();
        jLabelRSSLOGO = new javax.swing.JLabel();
        jSeparatorLINE = new javax.swing.JSeparator();
        jLabelMAINTABLE = new javax.swing.JLabel();
        jScrollPaneMAINTABLE = new javax.swing.JScrollPane();
        PlayListTABLE = new javax.swing.JTable();
        PlayListTABLE.setSelectionModel(new MainMenu.ForcedListSelectionModel());

        PlayListTABLE.getTableHeader().addMouseListener(new MainMenu.TableHeaderMouseListener(PlayListTABLE));
        jLabelFORM = new javax.swing.JLabel();
        jPanelFORM = new javax.swing.JPanel();
        txtName = new javax.swing.JTextField();
        txtCreationDate = new javax.swing.JTextField();
        txtDuration = new javax.swing.JTextField();
        jScrollPaneFORM = new javax.swing.JScrollPane();
        SongsTABLE = new javax.swing.JTable();
        SongsTABLE.setSelectionModel(new MainMenu.ForcedListSelectionModel());

        SongsTABLE.getTableHeader().addMouseListener(new MainMenu.TableHeaderMouseListener(SongsTABLE));
        jButtonADDSONG = new javax.swing.JButton();
        jButtonREMOVESONG = new javax.swing.JButton();
        jLabelNOTES = new javax.swing.JLabel();
        jLabelNANOPLAYER = new javax.swing.JLabel();
        jButtonIMPORT = new javax.swing.JButton();
        jButtonEXPORT = new javax.swing.JButton();
        jButtonRETURN = new javax.swing.JButton();
        jButtonADD = new javax.swing.JButton();
        jButtonEDIT = new javax.swing.JButton();
        jButtonREMOVE = new javax.swing.JButton();
        jButtonSAVE = new javax.swing.JButton();
        jButtonCANCEL = new javax.swing.JButton();
        jLabelBACKGROUND = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
        setTitle("RADIO STATION SYSTEM - Λίστες Τραγουδιών");
        setLocationByPlatform(true);
        setMinimumSize(new java.awt.Dimension(806, 604));
        setName("PLAYLISTsMANAGEMENT"); // NOI18N
        setResizable(false);
        addWindowFocusListener(new java.awt.event.WindowFocusListener() {
            public void windowGainedFocus(java.awt.event.WindowEvent evt) {
                formWindowGainedFocus(evt);
            }
            public void windowLostFocus(java.awt.event.WindowEvent evt) {
            }
        });
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosed(java.awt.event.WindowEvent evt) {
                formWindowClosed(evt);
            }
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing(evt);
            }
            public void windowOpened(java.awt.event.WindowEvent evt) {
                formWindowOpened(evt);
            }
        });
        getContentPane().setLayout(null);

        jLabelRSTEAM.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/RSTeam_maxi.png"))); // NOI18N
        jLabelRSTEAM.setFocusable(false);
        getContentPane().add(jLabelRSTEAM);
        jLabelRSTEAM.setBounds(516, 533, 284, 43);

        jLabelRSSLOGO.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/RSS-Logo_maxi.png"))); // NOI18N
        jLabelRSSLOGO.setFocusable(false);
        getContentPane().add(jLabelRSSLOGO);
        jLabelRSSLOGO.setBounds(12, 12, 180, 152);

        jSeparatorLINE.setAutoscrolls(true);
        getContentPane().add(jSeparatorLINE);
        jSeparatorLINE.setBounds(10, 534, 780, 2);

        jLabelMAINTABLE.setFont(new java.awt.Font("Arial", 1, 14)); // NOI18N
        jLabelMAINTABLE.setLabelFor(jScrollPaneMAINTABLE);
        jLabelMAINTABLE.setText("   ΛΙΣΤΕΣ ΤΡΑΓΟΥΔΙΩΝ");
        jLabelMAINTABLE.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jLabelMAINTABLE.setFocusable(false);
        jLabelMAINTABLE.setMaximumSize(new java.awt.Dimension(180, 20));
        jLabelMAINTABLE.setMinimumSize(new java.awt.Dimension(180, 20));
        jLabelMAINTABLE.setOpaque(true);
        jLabelMAINTABLE.setPreferredSize(new java.awt.Dimension(180, 20));
        jLabelMAINTABLE.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jLabelMAINTABLEMouseClicked(evt);
            }
        });
        getContentPane().add(jLabelMAINTABLE);
        jLabelMAINTABLE.setBounds(300, 30, 170, 20);

        jScrollPaneMAINTABLE.setFocusable(false);
        jScrollPaneMAINTABLE.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jScrollPaneMAINTABLE.setMinimumSize(new java.awt.Dimension(31, 31));
        jScrollPaneMAINTABLE.setPreferredSize(new java.awt.Dimension(450, 183));

        PlayListTABLE.setAutoCreateRowSorter(true);
        PlayListTABLE.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        PlayListTABLE.setMaximumSize(new java.awt.Dimension(2147483647, 0));
        PlayListTABLE.setMinimumSize(new java.awt.Dimension(332, 0));

        org.jdesktop.swingbinding.JTableBinding jTableBinding = org.jdesktop.swingbinding.SwingBindings.createJTableBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, listPlayList, PlayListTABLE);
        org.jdesktop.swingbinding.JTableBinding.ColumnBinding columnBinding = jTableBinding.addColumnBinding(org.jdesktop.beansbinding.ELProperty.create("${name}"));
        columnBinding.setColumnName("ΟΝΟΜΑ ΛΙΣΤΑΣ ΤΡΑΓΟΥΔΙΩΝ");
        columnBinding.setColumnClass(String.class);
        columnBinding.setEditable(false);
        columnBinding = jTableBinding.addColumnBinding(org.jdesktop.beansbinding.ELProperty.create("${creationdate}"));
        columnBinding.setColumnName("ΗΜΕΡΟΜΗΝΙΑ");
        columnBinding.setColumnClass(java.util.Date.class);
        columnBinding.setEditable(false);
        bindingGroup.addBinding(jTableBinding);
        jTableBinding.bind();
        PlayListTABLE.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                PlayListTABLEMouseClicked(evt);
            }
            public void mousePressed(java.awt.event.MouseEvent evt) {
                PlayListTABLEMousePressed(evt);
            }
            public void mouseReleased(java.awt.event.MouseEvent evt) {
                PlayListTABLEMouseReleased(evt);
            }
        });
        PlayListTABLE.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                PlayListTABLEKeyPressed(evt);
            }
            public void keyReleased(java.awt.event.KeyEvent evt) {
                PlayListTABLEKeyReleased(evt);
            }
        });
        jScrollPaneMAINTABLE.setViewportView(PlayListTABLE);
        if (PlayListTABLE.getColumnModel().getColumnCount() > 0) {
            PlayListTABLE.getColumnModel().getColumn(0).setMinWidth(187);
            PlayListTABLE.getColumnModel().getColumn(1).setMinWidth(123);
            PlayListTABLE.getColumnModel().getColumn(1).setPreferredWidth(163);
            PlayListTABLE.getColumnModel().getColumn(1).setMaxWidth(183);
            PlayListTABLE.getColumnModel().getColumn(1).setCellRenderer(new MainMenu.DateTimeCellRenderer());
        }

        getContentPane().add(jScrollPaneMAINTABLE);
        jScrollPaneMAINTABLE.setBounds(290, 50, 470, 183);

        jLabelFORM.setFont(new java.awt.Font("Arial", 1, 14)); // NOI18N
        jLabelFORM.setLabelFor(jPanelFORM);
        jLabelFORM.setText("    ΣΤΟΙΧΕΙΑ ΛΙΣΤΑΣ ΤΡΑΓΟΥΔΙΩΝ");
        jLabelFORM.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jLabelFORM.setEnabled(false);
        jLabelFORM.setFocusable(false);
        jLabelFORM.setMaximumSize(new java.awt.Dimension(180, 20));
        jLabelFORM.setMinimumSize(new java.awt.Dimension(180, 180));
        jLabelFORM.setOpaque(true);
        jLabelFORM.setPreferredSize(new java.awt.Dimension(180, 20));
        getContentPane().add(jLabelFORM);
        jLabelFORM.setBounds(50, 270, 240, 20);

        jPanelFORM.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jPanelFORM.setEnabled(false);
        jPanelFORM.setPreferredSize(new java.awt.Dimension(720, 180));
        jPanelFORM.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                jPanelFORMFocusGained(evt);
            }
        });
        jPanelFORM.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                jPanelFORMKeyPressed(evt);
            }
        });

        txtName.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        txtName.setBorder(javax.swing.BorderFactory.createTitledBorder("ΟΝΟΜΑ ΛΙΣΤΑΣ ΤΡΑΓΟΥΔΙΩΝ"));
        txtName.setEnabled(false);
        txtName.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                txtNameActionPerformed(evt);
            }
        });
        txtName.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                txtNameFocusGained(evt);
            }
            public void focusLost(java.awt.event.FocusEvent evt) {
                txtNameFocusLost(evt);
            }
        });
        txtName.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                txtNameKeyPressed(evt);
            }
        });

        txtCreationDate.setEditable(false);
        txtCreationDate.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        txtCreationDate.setBorder(javax.swing.BorderFactory.createTitledBorder("ΗΜ/ΝΙΑ ΜΕΤΑΒΟΛΗΣ-ΔΗΜΙΟΥΡΓΙΑΣ"));
        txtCreationDate.setEnabled(false);
        txtCreationDate.setFocusable(false);

        txtDuration.setEditable(false);
        txtDuration.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        txtDuration.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
        txtDuration.setBorder(javax.swing.BorderFactory.createTitledBorder("ΣΥΝ. ΔΙΑΡΚΕΙΑ"));
        txtDuration.setEnabled(false);
        txtDuration.setFocusable(false);

        jScrollPaneFORM.setBorder(javax.swing.BorderFactory.createTitledBorder("ΠΕΡΙΕΧΟΜΕΝΑ ΛΙΣΤΑΣ ΤΡΑΓΟΥΔΙΩΝ"));
        jScrollPaneFORM.setFocusable(false);
        jScrollPaneFORM.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jScrollPaneFORM.setMinimumSize(new java.awt.Dimension(31, 31));
        jScrollPaneFORM.setPreferredSize(new java.awt.Dimension(462, 107));

        SongsTABLE.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        SongsTABLE.setEnabled(false);
        SongsTABLE.setFocusable(false);
        SongsTABLE.setShowHorizontalLines(false);

        jTableBinding = org.jdesktop.swingbinding.SwingBindings.createJTableBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, listSongsInPlayList, SongsTABLE);
        columnBinding = jTableBinding.addColumnBinding(org.jdesktop.beansbinding.ELProperty.create("${title}"));
        columnBinding.setColumnName("ΤΙΤΛΟΣ ΤΡΑΓΟΥΔΙΟΥ");
        columnBinding.setColumnClass(String.class);
        columnBinding.setEditable(false);
        columnBinding = jTableBinding.addColumnBinding(org.jdesktop.beansbinding.ELProperty.create("${albumId}"));
        columnBinding.setColumnName("ΚΑΛΛΙΤΕΧΝΗΣ / ΣΥΓΚΡΟΤΗΜΑ");
        columnBinding.setColumnClass(rss_POJO.Album.class);
        columnBinding.setEditable(false);
        columnBinding = jTableBinding.addColumnBinding(org.jdesktop.beansbinding.ELProperty.create("${duration}"));
        columnBinding.setColumnName("ΔΙΑΡΚΕΙΑ");
        columnBinding.setColumnClass(Integer.class);
        columnBinding.setEditable(false);
        bindingGroup.addBinding(jTableBinding);
        jTableBinding.bind();
        SongsTABLE.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mousePressed(java.awt.event.MouseEvent evt) {
                SongsTABLEMousePressed(evt);
            }
        });
        SongsTABLE.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                SongsTABLEKeyPressed(evt);
            }
        });
        jScrollPaneFORM.setViewportView(SongsTABLE);
        if (SongsTABLE.getColumnModel().getColumnCount() > 0) {
            SongsTABLE.getColumnModel().getColumn(0).setMinWidth(141);
            SongsTABLE.getColumnModel().getColumn(1).setMinWidth(181);
            SongsTABLE.getColumnModel().getColumn(1).setCellRenderer(new MainMenu.AlbumIDCellRenderer());
            SongsTABLE.getColumnModel().getColumn(2).setMinWidth(68);
            SongsTABLE.getColumnModel().getColumn(2).setPreferredWidth(68);
            SongsTABLE.getColumnModel().getColumn(2).setMaxWidth(68);
            SongsTABLE.getColumnModel().getColumn(2).setCellRenderer(new MainMenu.TimeDurationCellRenderer());
        }

        jButtonADDSONG.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jButtonADDSONG.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/_Plus.png"))); // NOI18N
        jButtonADDSONG.setText("Προσθήκη");
        jButtonADDSONG.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jButtonADDSONG.setEnabled(false);
        jButtonADDSONG.setFocusable(false);
        jButtonADDSONG.setIconTextGap(8);
        jButtonADDSONG.setInheritsPopupMenu(true);
        jButtonADDSONG.setMargin(new java.awt.Insets(2, 0, 2, 0));
        jButtonADDSONG.setMaximumSize(new java.awt.Dimension(100, 30));
        jButtonADDSONG.setMinimumSize(new java.awt.Dimension(100, 30));
        jButtonADDSONG.setPreferredSize(new java.awt.Dimension(100, 30));
        jButtonADDSONG.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonADDSONGActionPerformed(evt);
            }
        });

        jButtonREMOVESONG.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jButtonREMOVESONG.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/_Minus.png"))); // NOI18N
        jButtonREMOVESONG.setText("Αφαίρεση");
        jButtonREMOVESONG.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jButtonREMOVESONG.setEnabled(false);
        jButtonREMOVESONG.setFocusable(false);
        jButtonREMOVESONG.setIconTextGap(8);
        jButtonREMOVESONG.setInheritsPopupMenu(true);
        jButtonREMOVESONG.setMargin(new java.awt.Insets(2, 0, 2, 0));
        jButtonREMOVESONG.setMaximumSize(new java.awt.Dimension(100, 30));
        jButtonREMOVESONG.setMinimumSize(new java.awt.Dimension(100, 30));
        jButtonREMOVESONG.setPreferredSize(new java.awt.Dimension(100, 30));
        jButtonREMOVESONG.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonREMOVESONGActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout jPanelFORMLayout = new javax.swing.GroupLayout(jPanelFORM);
        jPanelFORM.setLayout(jPanelFORMLayout);
        jPanelFORMLayout.setHorizontalGroup(
            jPanelFORMLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanelFORMLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanelFORMLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanelFORMLayout.createSequentialGroup()
                        .addComponent(txtName, javax.swing.GroupLayout.PREFERRED_SIZE, 460, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(18, 18, 18)
                        .addComponent(txtCreationDate, javax.swing.GroupLayout.PREFERRED_SIZE, 216, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(0, 0, Short.MAX_VALUE))
                    .addGroup(jPanelFORMLayout.createSequentialGroup()
                        .addComponent(jScrollPaneFORM, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addGap(18, 18, 18)
                        .addGroup(jPanelFORMLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                            .addComponent(jButtonREMOVESONG, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(jButtonADDSONG, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(txtDuration, javax.swing.GroupLayout.Alignment.TRAILING))))
                .addContainerGap())
        );
        jPanelFORMLayout.setVerticalGroup(
            jPanelFORMLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanelFORMLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanelFORMLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(txtName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(txtCreationDate, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addGroup(jPanelFORMLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                    .addComponent(jScrollPaneFORM, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addGroup(jPanelFORMLayout.createSequentialGroup()
                        .addComponent(txtDuration, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(1, 1, 1)
                        .addComponent(jButtonADDSONG, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addComponent(jButtonREMOVESONG, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        getContentPane().add(jPanelFORM);
        jPanelFORM.setBounds(40, 290, 720, 190);

        jLabelNOTES.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/~SongNotes.png"))); // NOI18N
        jLabelNOTES.setVerticalAlignment(javax.swing.SwingConstants.TOP);
        jLabelNOTES.setEnabled(false);
        jLabelNOTES.setFocusable(false);
        jLabelNOTES.setMaximumSize(new java.awt.Dimension(0, 0));
        jLabelNOTES.setMinimumSize(new java.awt.Dimension(0, 0));
        getContentPane().add(jLabelNOTES);
        jLabelNOTES.setBounds(140, 150, 128, 120);

        jLabelNANOPLAYER.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/~PlayListNano.png"))); // NOI18N
        jLabelNANOPLAYER.setVerticalAlignment(javax.swing.SwingConstants.TOP);
        jLabelNANOPLAYER.setEnabled(false);
        jLabelNANOPLAYER.setFocusable(false);
        jLabelNANOPLAYER.setMaximumSize(new java.awt.Dimension(0, 0));
        jLabelNANOPLAYER.setMinimumSize(new java.awt.Dimension(0, 0));
        getContentPane().add(jLabelNANOPLAYER);
        jLabelNANOPLAYER.setBounds(100, 170, 130, 110);

        jButtonIMPORT.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jButtonIMPORT.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/_Open.png"))); // NOI18N
        jButtonIMPORT.setText("Εισαγωγή Λίστας");
        jButtonIMPORT.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jButtonIMPORT.setEnabled(false);
        jButtonIMPORT.setFocusable(false);
        jButtonIMPORT.setIconTextGap(12);
        jButtonIMPORT.setInheritsPopupMenu(true);
        jButtonIMPORT.setMargin(new java.awt.Insets(2, 0, 2, 0));
        jButtonIMPORT.setMaximumSize(new java.awt.Dimension(120, 30));
        jButtonIMPORT.setMinimumSize(new java.awt.Dimension(120, 30));
        jButtonIMPORT.setPreferredSize(new java.awt.Dimension(120, 30));
        jButtonIMPORT.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonIMPORTActionPerformed(evt);
            }
        });
        getContentPane().add(jButtonIMPORT);
        jButtonIMPORT.setBounds(40, 490, 140, 30);

        jButtonEXPORT.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jButtonEXPORT.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/_Save.png"))); // NOI18N
        jButtonEXPORT.setText("Αποθήκευση Λίστας");
        jButtonEXPORT.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jButtonEXPORT.setEnabled(false);
        jButtonEXPORT.setFocusable(false);
        jButtonEXPORT.setIconTextGap(12);
        jButtonEXPORT.setInheritsPopupMenu(true);
        jButtonEXPORT.setMargin(new java.awt.Insets(2, 0, 2, 0));
        jButtonEXPORT.setMaximumSize(new java.awt.Dimension(120, 30));
        jButtonEXPORT.setMinimumSize(new java.awt.Dimension(120, 30));
        jButtonEXPORT.setPreferredSize(new java.awt.Dimension(120, 30));
        jButtonEXPORT.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonEXPORTActionPerformed(evt);
            }
        });
        getContentPane().add(jButtonEXPORT);
        jButtonEXPORT.setBounds(190, 490, 160, 30);

        jButtonRETURN.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jButtonRETURN.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/_Return.png"))); // NOI18N
        jButtonRETURN.setText("Επιστροφή");
        jButtonRETURN.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jButtonRETURN.setFocusable(false);
        jButtonRETURN.setIconTextGap(8);
        jButtonRETURN.setMargin(new java.awt.Insets(2, 0, 2, 0));
        jButtonRETURN.setMaximumSize(new java.awt.Dimension(100, 30));
        jButtonRETURN.setMinimumSize(new java.awt.Dimension(100, 30));
        jButtonRETURN.setPreferredSize(new java.awt.Dimension(110, 30));
        jButtonRETURN.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonRETURNActionPerformed(evt);
            }
        });
        getContentPane().add(jButtonRETURN);
        jButtonRETURN.setBounds(40, 541, 110, 30);

        jButtonADD.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jButtonADD.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/_Add.png"))); // NOI18N
        jButtonADD.setText("Εισαγωγή");
        jButtonADD.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jButtonADD.setFocusable(false);
        jButtonADD.setIconTextGap(12);
        jButtonADD.setInheritsPopupMenu(true);
        jButtonADD.setMargin(new java.awt.Insets(2, 0, 2, 0));
        jButtonADD.setMaximumSize(new java.awt.Dimension(100, 30));
        jButtonADD.setMinimumSize(new java.awt.Dimension(100, 30));
        jButtonADD.setPreferredSize(new java.awt.Dimension(100, 30));
        jButtonADD.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonADDActionPerformed(evt);
            }
        });
        getContentPane().add(jButtonADD);
        jButtonADD.setBounds(420, 243, 100, 30);

        jButtonEDIT.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jButtonEDIT.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/_Edit.png"))); // NOI18N
        jButtonEDIT.setText("Επεξεργασία");
        jButtonEDIT.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jButtonEDIT.setFocusable(false);
        jButtonEDIT.setIconTextGap(12);
        jButtonEDIT.setInheritsPopupMenu(true);
        jButtonEDIT.setMargin(new java.awt.Insets(2, 0, 2, 0));
        jButtonEDIT.setMaximumSize(new java.awt.Dimension(120, 30));
        jButtonEDIT.setMinimumSize(new java.awt.Dimension(120, 30));
        jButtonEDIT.setPreferredSize(new java.awt.Dimension(120, 30));
        jButtonEDIT.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonEDITActionPerformed(evt);
            }
        });
        getContentPane().add(jButtonEDIT);
        jButtonEDIT.setBounds(530, 243, 120, 30);

        jButtonREMOVE.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jButtonREMOVE.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/_Remove.png"))); // NOI18N
        jButtonREMOVE.setText("Διαγραφή");
        jButtonREMOVE.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jButtonREMOVE.setFocusable(false);
        jButtonREMOVE.setIconTextGap(12);
        jButtonREMOVE.setInheritsPopupMenu(true);
        jButtonREMOVE.setMargin(new java.awt.Insets(2, 0, 2, 0));
        jButtonREMOVE.setMaximumSize(new java.awt.Dimension(100, 30));
        jButtonREMOVE.setMinimumSize(new java.awt.Dimension(100, 30));
        jButtonREMOVE.setPreferredSize(new java.awt.Dimension(100, 30));
        jButtonREMOVE.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonREMOVEActionPerformed(evt);
            }
        });
        getContentPane().add(jButtonREMOVE);
        jButtonREMOVE.setBounds(660, 243, 100, 30);

        jButtonSAVE.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jButtonSAVE.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/_Check.png"))); // NOI18N
        jButtonSAVE.setText("Αποθήκευση");
        jButtonSAVE.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jButtonSAVE.setEnabled(false);
        jButtonSAVE.setFocusable(false);
        jButtonSAVE.setIconTextGap(12);
        jButtonSAVE.setInheritsPopupMenu(true);
        jButtonSAVE.setMargin(new java.awt.Insets(2, 0, 2, 0));
        jButtonSAVE.setMaximumSize(new java.awt.Dimension(120, 30));
        jButtonSAVE.setMinimumSize(new java.awt.Dimension(120, 30));
        jButtonSAVE.setPreferredSize(new java.awt.Dimension(120, 30));
        jButtonSAVE.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonSAVEActionPerformed(evt);
            }
        });
        getContentPane().add(jButtonSAVE);
        jButtonSAVE.setBounds(530, 490, 120, 30);

        jButtonCANCEL.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
        jButtonCANCEL.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/_Cancel.png"))); // NOI18N
        jButtonCANCEL.setText("Ακύρωση");
        jButtonCANCEL.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        jButtonCANCEL.setEnabled(false);
        jButtonCANCEL.setFocusable(false);
        jButtonCANCEL.setIconTextGap(12);
        jButtonCANCEL.setInheritsPopupMenu(true);
        jButtonCANCEL.setMargin(new java.awt.Insets(2, 0, 2, 0));
        jButtonCANCEL.setMaximumSize(new java.awt.Dimension(100, 30));
        jButtonCANCEL.setMinimumSize(new java.awt.Dimension(100, 30));
        jButtonCANCEL.setPreferredSize(new java.awt.Dimension(100, 30));
        jButtonCANCEL.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonCANCELActionPerformed(evt);
            }
        });
        getContentPane().add(jButtonCANCEL);
        jButtonCANCEL.setBounds(660, 490, 100, 30);

        jLabelBACKGROUND.setIcon(new javax.swing.ImageIcon(getClass().getResource("/misc/design/MICROPHONE_maxi.png"))); // NOI18N
        jLabelBACKGROUND.setFocusable(false);
        jLabelBACKGROUND.setPreferredSize(new java.awt.Dimension(800, 576));
        getContentPane().add(jLabelBACKGROUND);
        jLabelBACKGROUND.setBounds(0, 0, 800, 576);

        bindingGroup.bind();

        pack();
    }// </editor-fold>//GEN-END:initComponents
   
    private void formWindowOpened(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowOpened
        ClearAllFields();  // καθαρισμός όλων των πεδίων της φόρμας επεξεργασίας
        System.out.println("~PlayListManagement is ON");
    }//GEN-LAST:event_formWindowOpened

    private void formWindowGainedFocus(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowGainedFocus
        // κάθε φορά που επικεντρώνω στο παράθυρο επικαιροποιώ και την επικεφαλίδα του
        if(mm.getExpertMode()) this.setTitle(frameTitle + " (ExpertMode)"); else this.setTitle(frameTitle);
    }//GEN-LAST:event_formWindowGainedFocus

    private void jButtonRETURNActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonRETURNActionPerformed
        System.out.println(" ~ButtonReturn");
        if(editStatus) {
            editStatus = false;  // για προστασία των πεδίων από τυχόν αλλαγές
            // αν έχουν συμβεί αλλαγές και δεν είμαστε σε ExpertMode, επιλογή okcancelBox για επιστροφή στη φόρμα 
            if(!compareAllFields() && !mm.getExpertMode() && !(rss.okcancelBox(this, "Οι αλλαγές θα χαθούν. Είστε σίγουροι; ") == 0)) {editStatus = true; return; }
        }    
        if(ast != null) ast.dispose(); this.toBack(); dispose();  // στείλε το παράθυρο στο background και κλείσε το PlayListManagement
        System.out.println(" ~closing PlayListManagement by Button");
    }//GEN-LAST:event_jButtonRETURNActionPerformed

    private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
        System.out.println(" ~ButtonX");
        if(editStatus) {
            editStatus = false;  // για προστασία των πεδίων από τυχόν αλλαγές
            // αν έχουν συμβεί αλλαγές και δεν είμαστε σε ExpertMode, επιλογή okcancelBox για επιστροφή στη φόρμα 
            if(!compareAllFields() && !mm.getExpertMode() && !(rss.okcancelBox(this, "Οι αλλαγές θα χαθούν. Είστε σίγουροι; ") == 0)) {editStatus = true; return; }
        }
        if(ast != null) ast.dispose(); this.toBack(); dispose();  // στείλε το παράθυρο στο background και κλείσε το PlayListManagement
        System.out.println(" ~closing PlayListManagement by X");
    }//GEN-LAST:event_formWindowClosing

    private void formWindowClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosed
        mm.setEnabled(true);  // έξοδος και ενεργοποίηση της MainMenu
        System.out.println("~PlayListManagement is now OFF");
    }//GEN-LAST:event_formWindowClosed

    private void jLabelMAINTABLEMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jLabelMAINTABLEMouseClicked
        if(!editStatus) PlayListTABLE.getRowSorter().setSortKeys(null);  // κατάργηση ταξινόμησης
        // εμφάνιση viewport που να περιέχει την επιλεγμένη γραμμή και επικαιροποίηση του πίνακα
        PlayListTABLE.scrollRectToVisible(PlayListTABLE.getCellRect(PlayListTABLE.getSelectedRow(), 0, true));
        PlayListTABLE.repaint();
    }//GEN-LAST:event_jLabelMAINTABLEMouseClicked

    private void PlayListTABLEKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_PlayListTABLEKeyPressed
        //System.out.println(evt.getKeyCode());
        if(evt.getKeyCode() == 32) { evt.setKeyCode(10); }  // επαναχαρτογράφηση Space key στο Enter key
        else if(evt.getKeyCode() == 127) jButtonREMOVEActionPerformed(null);  // ανακατεύθυνση του Delete key
        else if(evt.getKeyCode() == 10) jButtonEDITActionPerformed(null);  // ανακατεύθυνση του Enter key
        PlayListTABLEKeyReleased(null);  // ίδιος κώδικας με PlayListTABLEKeyReleased
    }//GEN-LAST:event_PlayListTABLEKeyPressed

    private void PlayListTABLEKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_PlayListTABLEKeyReleased
        int row = PlayListTABLE.getSelectedRow();
        // έλεγχος επιλογής γραμμής πίνακα Τραγουδιών
        if(row != -1) {  // αν ο πίνακας δεν είναι άδειος
            // αντιστοίχιση pl τύπου PlayList στην επιλεγμένη γραμμή
            pl = listPlayList.get(PlayListTABLE.convertRowIndexToModel(row));
            UpdateAllFields();  // ενημέρωση πεδίων φόρμας επεξεργασίας με τις τιμές του επιλεγμένου αντικειμένου
        } else {
            PlayListTABLE.clearSelection(); ClearAllFields();  // clear επιλογής και πεδίων
        }
    }//GEN-LAST:event_PlayListTABLEKeyReleased

    private void PlayListTABLEMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_PlayListTABLEMouseClicked
        if(!editStatus) {
            // εξυπηρέτηση single click
            if(evt.getClickCount() == 1) { /*PlayListTABLEKeyReleased(null);*/ }  // τίποτα ή εναλλακτικά ίδιος κώδικας με PlayListTABLEKeyReleased
            // εξυπηρέτηση double click με μετάβαση σε κατάσταση επεξεργασίας
            else if(evt.getClickCount() == 2) jButtonEDITActionPerformed(null);
        }
    }//GEN-LAST:event_PlayListTABLEMouseClicked

    private void PlayListTABLEMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_PlayListTABLEMouseReleased
        if(!editStatus) {
            PlayListTABLEKeyReleased(null);  // ίδιος κώδικας με PlayListTABLEKeyReleased
        }
    }//GEN-LAST:event_PlayListTABLEMouseReleased

    private void jButtonADDActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonADDActionPerformed
        if(!editStatus) {  // αν όχι σε κατάστασης επεξεργασίας
            System.out.println(" ~ButtonADD");
            // προετοιμασία ενεργών & ανενεργών components για μετάβαση σε κατάσταση επεξεργασίας
            ClearAllFields(); EnableDisableFields(true); jButtonADD.setEnabled(true); jPanelFORM.grabFocus();
            // νέα pl τύπου PlayList και κατάσταση επεξεργασίας ADD
            pl = new PlayList(); addORedit = true;
            //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        } else Toolkit.getDefaultToolkit().beep();
    }//GEN-LAST:event_jButtonADDActionPerformed

    private void jButtonEDITActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonEDITActionPerformed
        if(!editStatus) {  // αν όχι σε κατάσταση επεξεργασίας
            System.out.println(" ~ButtonEDIT");
            if(PlayListTABLE.getSelectedRow() != -1) {  // και ο πίνακας δεν είναι άδειος
                int row = PlayListTABLE.getSelectedRow();  // αποθήκευση επιλεγμένης γραμμής
                // αντιστοίχιση αντικειμένου pl τύπου PlayList στην επιλεγμένη γραμμή, προσωρινή αποθήκευση 
                pl = listPlayList.get(PlayListTABLE.convertRowIndexToModel(row)); CopyAllFields(tempPL, pl);
                if(rss.accessDB(this, pl,"R")) {  // και έλεγχος/επικαιροποίηση των πεδίων του αντικειμένου pl από την ΒΔ as good measure
                    // αν όλα πήγαν καλά, προετοιμασία ενεργών & ανενεργών components και ανανέωση πίνακα Τραγουδιών
                    EnableDisableFields(true); jButtonEDIT.setEnabled(true); jPanelFORM.grabFocus(); //SongsTABLE.repaint();
                    // νέα προσωρινή αποθήκευση, ενημέρωση πεδίων με τις τιμές του πίνακα Λίστας Τραγουδιών και κατάσταση επεξεργασίας EDIT
                    CopyAllFields(tempPL, pl); UpdateAllFields(); addORedit = false;
                    //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                } else {  // αν όμως συμβεί σφάλμά κατά τον έλεγχος/επικαιροποίηση των πεδίων του αντικειμένου pl από την ΒΔ
                    // εκ νέου αντιστοίχιση αντικειμένου pl τύπου PlayList στην επιλεγμένη γραμμή και επαναφορά
                    pl = listPlayList.get(PlayListTABLE.convertRowIndexToModel(row)); CopyAllFields(pl, tempPL);
                    UpdateAllFields(); addORedit = null;  // ενημέρωση πεδίων με τις τιμές του πίνακα
                    //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                }
            } else {  // αλλιώς αν ο πίνακας είναι άδειος
                PlayListTABLE.clearSelection(); ClearAllFields();  // clear επιλογής πίνακα και πεδίων
                Toolkit.getDefaultToolkit().beep();
                //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            }
        } else Toolkit.getDefaultToolkit().beep();
    }//GEN-LAST:event_jButtonEDITActionPerformed

    private void jButtonREMOVEActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonREMOVEActionPerformed
        System.out.println(" ~ButtonREMOVE");
        if(PlayListTABLE.getSelectedRow() != -1) {  // αν ο πίνακας δεν είναι άδειος
            int row = PlayListTABLE.getSelectedRow();  // αποθήκευση επιλεγμένης γραμμής
            if(!mm.getExpertMode() && !(rss.okcancelBox(this, "Η επιλεγμένη γραμμή θα διαγραφεί. Είστε σίγουροι;  ") == 0)) return;
                //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            // αντιστοίχιση αντικειμένου pl τύπου PlayList στην επιλεγμένη γραμμή του πίνακα, προσωρινή αποθήκευση
            pl = listPlayList.get(PlayListTABLE.convertRowIndexToModel(row)); CopyAllFields(tempPL, pl);
            if(rss.accessDB(this, pl,"R")) {  // έλεγχος ύπαρξής/επικαιροποίηση as good measure του αντικειμένου pl από την ΒΔ
                CopyAllFields(tempPL, pl);  // εκ νέου προσωρινή αποθήκευση πιθανώς επικαιροποιημένου αντικειμένου από προηγουμένως
                if(rss.accessDB(this, pl,"D")) {  // διαγραφή του αντικειμένου pl από την ΒΔ
                    // όλα καλά, διαγραφή αντικειμένου pl και από τη λίστα της φόρμας, νέα pl PlayList και η παλιά έτοιμη για GarbageCollector!
                    listPlayList.remove(pl); clearPlayList(pl); pl = null; pl = new PlayList();
                    // επικαιροποίηση επιλογής γραμμής πίνακα Τραγουδιών λόγω διαγραφής
                    if(row >= PlayListTABLE.getRowCount()) row = PlayListTABLE.getRowCount()-1;
                    // έλεγχος επιλογής γραμμής πίνακα Λίστας Τραγουδιών για επικαιροποίηση πεδίων λόγω διαγραφής
                    if(row != -1) {  // αν ο πίνακας που προέκυψε δεν είναι άδειος
                        PlayListTABLE.setRowSelectionInterval(row, row);  // επέλεξε την νέα γραμμή
                        pl = listPlayList.get(PlayListTABLE.convertRowIndexToModel(row));  // αντιστοίχιση pl τύπου PlayList στην επιλεγμένη γραμμή 
                        UpdateAllFields();  // ενημέρωση πεδίων με τις τιμές του επιλεγμένου αντικειμένου
                    } else {  // και αν ο πίνακας που προέκυψε είναι άδειος
                        PlayListTABLE.clearSelection(); ClearAllFields();  // clear επιλογής πίνακα και πεδίων
                    }
                    // επιλογή νέου viewport και επικαιροποίηση του πίνακα Λίστας Τραγουδιών
                    PlayListTABLE.scrollRectToVisible(PlayListTABLE.getCellRect(row, 0, true)); //PlayListTABLE.repaint();
                    //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                } else {  // και αν γενικότερα συμβεί κάποιο σφάλμα κατά τη διαγραφή του αντικειμένου pl από την ΒΔ
                    // εκ νέου αντιστοίχιση αντικειμένου pl τύπου PlayList στην επιλεγμένη γραμμή και επαναφορά
                    pl = listPlayList.get(PlayListTABLE.convertRowIndexToModel(row)); CopyAllFields(pl, tempPL);
                    UpdateAllFields();  // ενημέρωση πεδίων με τις τιμές του επιλεγμένου αντικειμένου
                    //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                }
            } else {  // και αν συμβεί σφάλμα κατά τον έλεγχο ύπαρξής/επικαιροποίηση του αντικειμένου pl από την ΒΔ
                // εκ νέου αντιστοίχιση αντικειμένου pl τύπου PlayList στην επιλεγμένη γραμμή και επαναφορά
                pl = listPlayList.get(PlayListTABLE.convertRowIndexToModel(row)); CopyAllFields(pl, tempPL);
                UpdateAllFields();  // ενημέρωση πεδίων με τις τιμές του επιλεγμένου αντικειμένου
                //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            }
        } else {  // και αν ο πίνακας είναι άδειος
            PlayListTABLE.clearSelection(); ClearAllFields();  // clear επιλογής και πεδίων
            Toolkit.getDefaultToolkit().beep();
        }
    }//GEN-LAST:event_jButtonREMOVEActionPerformed

    private void jButtonSAVEActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonSAVEActionPerformed
        System.out.println("  ~ButtonSAVE");
        editStatus = false;  // προστασία των πεδίων από τυχόν αλλαγές
        if(addORedit) {  // αν βρισκόμαστε σε κατάσταση επεξεργασίας ADD
            // επαλήθευση πεδίων και εγγραφή pl στη ΒΔ
            if(validateAllFields() && rss.accessDB(this, pl,"W")) {  // αν η επιβεβαίωση και η εγγραφή γίνουν σωστά
                listPlayList.add(pl);  // όλα καλά, προσθήκη του αντικειμένου και στη λίστα
                int row = PlayListTABLE.getRowCount()-1;  // κατέγραψε το νέο μέγεθος του πίνακα
                // αποθήκευσε πιθανή κατάσταση ταξινόμησης και κατάργησε το row sorting του πίνακα
                List<? extends SortKey> myList = PlayListTABLE.getRowSorter().getSortKeys(); PlayListTABLE.getRowSorter().setSortKeys(null);
                PlayListTABLE.setRowSelectionInterval(row, row);  //επέλεξε τη νέα γραμμή (τελευταία σε unsorted model) του πίνακα
                // επαναφορά αποθηκευμένης κατάστασης ταξινόμησης και ρύθμιση viewport πίνακα ώστε η νεοεισηγμένη γραμμή να είναι visible
                PlayListTABLE.getRowSorter().setSortKeys(myList); PlayListTABLE.scrollRectToVisible(PlayListTABLE.getCellRect(row, 0, true));
                // αντιστοίχιση αντικειμένου pl τύπου PlayList στην επιλεγμένη γραμμή του πίνακα
                pl = listPlayList.get(PlayListTABLE.convertRowIndexToModel(PlayListTABLE.getSelectedRow()));
                // ενημέρωση πεδίων με τις τιμές του επιλεγμένου αντικειμένου και επικαιροποίηση του πίνακα Λίστας Τραγουδιών
                if(ast != null) ast.dispose(); UpdateAllFields(); //PlayListTABLE.repaint();
                // προετοιμασία ενεργών & ανενεργών components και reset κατάστασης επεξεργασίας
                EnableDisableFields(false); PlayListTABLE.grabFocus(); addORedit = null;
                //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
            } else { clearPlayList(pl); pl = null; pl = new PlayList(); editStatus = true; }  // κάτι δεν έγινε σωστά, νέα καθαρή PlayList pl και πίσω στη φόρμα
        } else {  // και αν βρισκόμαστε σε κατάσταση επεξεργασίας EDIT
            // επαλήθευση πεδίων και ανανέωση pl στη ΒΔ
            if(validateAllFields() && rss.accessDB(this, pl,"U")) { // αν η επιβεβαίωση και η ανανέωση γίνουν σωστά
                // αν όλα καλά, ενημέρωση του αντικειμένου pl και στη λίστα
                listPlayList.set(PlayListTABLE.convertRowIndexToModel(PlayListTABLE.getSelectedRow()), pl);
                // και εκ νέου αντιστοίχιση αντικειμένου pl τύπου PlayList στην επιλεγμένη γραμμή του πίνακα
                pl = listPlayList.get(PlayListTABLE.convertRowIndexToModel(PlayListTABLE.getSelectedRow()));
                // ενημέρωση πεδίων με τις τιμές του επιλεγμένου αντικειμένου και επικαιροποίηση του πίνακα Λίστας Τραγουδιών
                if(ast != null) ast.dispose(); UpdateAllFields(); //PlayListTABLE.repaint();
                // προετοιμασία ενεργών & ανενεργών components και reset κατάστασης επεξεργασίας
                EnableDisableFields(false); PlayListTABLE.grabFocus(); addORedit = null;
                //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
            } else {  // κάτι δεν έγινε σωστά, ξαναδιάβασμα pl από τον πίνακα και πίσω στη φόρμα
                // εκ νέου αντιστοίχιση αντικειμένου pl τύπου PlayList στην επιλεγμένη γραμμή του πίνακα
                pl = listPlayList.get(PlayListTABLE.convertRowIndexToModel(PlayListTABLE.getSelectedRow()));
                // και νέος έλεγχος-ανανέωση των πεδίων του από την ΒΔ
                if(rss.accessDB(this, pl,"R")) {  // αν η νέα ανάγνωση γίνει σωστά
                    CopyAllFields(tempPL, pl); editStatus = true;  // νέα προσωρινή αποθήκευση και πίσω στη φόρμα επεξεργασίας
                    //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                } else {  // το σφάλμα είναι μάλλον σοβαρότερο, πίσω πάλι στον πίνακα    
                    // εκ νέου αντιστοίχιση as good measure αντικειμένου pl τύπου PlayList στην επιλεγμένη γραμμή του πίνακα και επαναφορά
                    pl = listPlayList.get(PlayListTABLE.convertRowIndexToModel(PlayListTABLE.getSelectedRow())); CopyAllFields(pl, tempPL);
                    // ενημέρωση πεδίων με τις τιμές του επιλεγμένου αντικειμένου και επικαιροποίηση του πίνακα Τραγουδιών
                    if(ast != null) ast.dispose(); UpdateAllFields(); //PlayListTABLE.repaint();
                    // προετοιμασία ενεργών & ανενεργών components και reset κατάστασης επεξεργασίας
                    EnableDisableFields(false); PlayListTABLE.grabFocus(); addORedit = null;
                    //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                }
            }
        }
    }//GEN-LAST:event_jButtonSAVEActionPerformed

    private void jButtonCANCELActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonCANCELActionPerformed
        System.out.println("  ~ButtonCANCELorESCAPE");
        editStatus = false;  // για προστασία των πεδίων από τυχόν αλλαγές
        // αν έχουν συμβεί αλλαγές και δεν είμαστε σε ExpertMode, επιλογή okcancelBox για επιστροφή στη φόρμα
        if(!compareAllFields() && !mm.getExpertMode() && !(rss.okcancelBox(this, "Οι αλλαγές θα χαθούν. Είστε σίγουροι; ") == 0)) { editStatus = true; return; }
            //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        // αν ακυρώθηκε ADD η pl που δημιουργήθηκε πριν δεν χρειάζεται παρά μόνο στον GarbageCollector, αλλιώς as good measure επαναφορά προσωρινής
        if(addORedit) { clearPlayList(pl); pl = null; pl = new PlayList(); } else CopyAllFields(pl, tempPL);
        // τώρα που πλέον η ακύρωση έχει ολοκληρωθεί, πάμε καλόυ κακού για ανανέωση του πίνακα
        if(PlayListTABLE.getSelectedRow() != -1)  {  // αν ο πίνακας δεν είναι άδειος
            // αντιστοίχιση αντικειμένου pl τύπου PlayList στην επιλεγμένη γραμμή του πίνακα
            pl = listPlayList.get(PlayListTABLE.convertRowIndexToModel(PlayListTABLE.getSelectedRow()));
            UpdateAllFields();  // ενημέρωση πεδίων με τις τιμές του επιλεγμένου αντικειμένου
        } else {  // και αν ο πίνακας είναι άδειος
            PlayListTABLE.clearSelection(); ClearAllFields();  // clear επιλογής πίνακα και πεδίων
        }
        // προετοιμασία ενεργών & ανενεργών components και reset κατάστασης επεξεργασίας
        if(ast != null) ast.dispose(); EnableDisableFields(false); PlayListTABLE.grabFocus(); addORedit = null;
        //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    }//GEN-LAST:event_jButtonCANCELActionPerformed

    private void PlayListTABLEMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_PlayListTABLEMousePressed
        if(!editStatus) {
            PlayListTABLEKeyReleased(null);  // ίδιος κώδικας με PlayListTABLEKeyReleased
        }
    }//GEN-LAST:event_PlayListTABLEMousePressed

    private void jPanelFORMKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_jPanelFORMKeyPressed
        if(evt.getKeyCode() == 27) jButtonCANCELActionPerformed(null);  // έξοδος από το jPanel με Escape
    }//GEN-LAST:event_jPanelFORMKeyPressed

    private void jPanelFORMFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_jPanelFORMFocusGained
        System.out.println(" ~FocusGained jpanelform " + fieldFocused);
        fieldFocused = 0;
    }//GEN-LAST:event_jPanelFORMFocusGained

    private void jButtonREMOVESONGActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonREMOVESONGActionPerformed
        System.out.println("  ~ButtonREMOVEMEMBER");
        if(SongsTABLE.getSelectedRow() != -1) {  // αν ο πίνακας δεν είναι άδειος
            int row = SongsTABLE.getSelectedRow();  // αποθήκευση επιλεγμένης γραμμής
            if(!mm.getExpertMode() && !(rss.okcancelBox(this, "Η επιλεγμένη γραμμή θα διαγραφεί. Είστε σίγουροι;  ") == 0)) return;
            //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            // διαγραφή επιλεγμένου αντικειμένου τύπου Song από τη λίστα
            listSongsInPlayList.remove(listSongsInPlayList.get(SongsTABLE.convertRowIndexToModel(row)));
            // επικαιροποίηση επιλογής γραμμής πίνακα Τραγουδιών λόγω διαγραφής γραμμής
            if(row >= SongsTABLE.getRowCount()) row = SongsTABLE.getRowCount()-1;
            // έλεγχος επιλογής γραμμής πίνακα Τραγουδιών για επικαιροποίηση πεδίων λόγω διαγραφής
            if(row != -1) {  // αν ο πίνακας που προέκυψε δεν είναι άδειος
                SongsTABLE.setRowSelectionInterval(row, row);  // επέλεξε την νέα γραμμή
            } else {  // αν όμως ο πίνακας που προέκυψε είναι άδειος
                SongsTABLE.clearSelection();  // clear επιλογής
            }
            // επιλογή νέου viewport και επικαιροποίηση του πίνακα Τραγουδιών
            SongsTABLE.scrollRectToVisible(SongsTABLE.getCellRect(row, 0, true)); //ArtistsTABLE.repaint();
            if(listSongsInPlayList == null || listSongsInPlayList.isEmpty()) txtDuration.setText("");  // τίποτα αν είναι κενή η Λίστα Τραγουδιών
            else { int totalDuration = 0; for(Song s : listSongsInPlayList) totalDuration += s.getDuration(); txtDuration.setText(rss.integerToTimeDuration(totalDuration) + " "); }  // νέος υπολογισμός συνολικής διάρκειας Τραγουδιών της Λίστας
            //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        } else {  // και αν ο πίνακας είναι άδειος
            SongsTABLE.clearSelection();  // clear επιλογής πίνακα
            Toolkit.getDefaultToolkit().beep();
        }
    }//GEN-LAST:event_jButtonREMOVESONGActionPerformed

    private void jButtonADDSONGActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonADDSONGActionPerformed
        System.out.println("  ~ButtonADDMEMBER");
        if(ast == null) {  // δημιουργία αντικειμένου ast τύπου AvailableSongsTable με τοποθέτηση
            ast = new AvailableSongsTable(this, mm);
            ast.setLocationRelativeTo(jLabelNOTES);
        }  
        ast.setVisible(true);  // και το εμφανίζουμε
        ast.toFront();
    }//GEN-LAST:event_jButtonADDSONGActionPerformed

    private void SongsTABLEKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_SongsTABLEKeyPressed
        //System.out.println(evt.getKeyCode());
        if(evt.getKeyCode() == 32) { evt.setKeyCode(10); }  // επαναχαρτογράφηση Space key στο Enter key
        else if(evt.getKeyCode() == 127) jButtonREMOVESONGActionPerformed(null);  // ανακατεύθυνση του Delete key
    }//GEN-LAST:event_SongsTABLEKeyPressed

    private void SongsTABLEMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_SongsTABLEMousePressed
        if(editStatus && !SongsTABLE.isFocusable()) {
            SongsTABLE.setFocusable(true); SongsTABLE.grabFocus();  // focus στον πίνακα
            jButtonREMOVESONG.setEnabled(true);  // ενεργοποίηση κουμπιού αφαίρεσης Τραγουδιού
        }
    }//GEN-LAST:event_SongsTABLEMousePressed

    private void txtNameKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtNameKeyPressed
        if(evt.getKeyCode() == 27) {  // αν πατηθεί Escape επαναφορά στην προηγούμενη έγκυρη κατάσταση προσέχοντας και πιθανά nulls
            // αν συνέβησαν αλλαγές στο πεδίο και δεν είναι ήδη ολόκληρο επιλεγμένο
            if(!tempTxt.equals(txtName.getText()) || !tempTxt.equals(txtName.getSelectedText() == null? "" : txtName.getSelectedText())) {
                // επαναφορά προηγούμενου και highlighted επιλογή
                txtName.setText(tempTxt); txtName.selectAll();
            } else jButtonCANCELActionPerformed(null);  // αλλιώς έξοδος από τη φόρμα επεξεργασίας
        }
    }//GEN-LAST:event_txtNameKeyPressed

    private void txtNameFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtNameFocusLost
        System.out.println("  ~FocusLost name");
        if(editStatus && !evt.isTemporary()) {  // αν όχι προσωρινό focus lost
            txtNameActionPerformed(null); fieldFocused = 0;  // έλεγχος εγκυρότητας του αλφαριθμητικού μέσω txtNameActionPerformed
            System.out.println("   ~gotoActionPerformed & focused:0");
        }
    }//GEN-LAST:event_txtNameFocusLost

    private void txtNameFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtNameFocusGained
        System.out.println("  ~FocusGained name " + fieldFocused);
        if(fieldFocused == 0) {  // αν δεν είναι ήδη focused πεδίο
            // clear επιλεγμένης γραμμής πίνακα και απενεργοποίηση focus
            SongsTABLE.clearSelection(); SongsTABLE.setFocusable(false);
            jButtonREMOVESONG.setEnabled(false);  // απενεργοποίηση κουμπιού αφαίρεσης Τραγουδιού
            // εφόσον δεν πρόκειται για ήδη focused πεδίο, σώσε το προσωρινά και highlighted επιλογή
            tempTxt = txtName.getText(); txtName.selectAll(); fieldFocused = 1;
            System.out.println("   ~selectAll & focused:1");
        }
    }//GEN-LAST:event_txtNameFocusGained

    private void txtNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtNameActionPerformed
        System.out.println("  ~ActionPerformed name");
        // αν υπάρχουν ανεπιθύμητοι \ χαρακτήρες, grab focus και warningBox
        if(!mm.getExpertMode() && !txtName.getText().equals(txtName.getText().replace("\\",""))) {
            txtName.grabFocus(); rss.infoBox(this, "Δεν επιτρέπονται  \\  σε αλφαριθμητικά"); return;
            // αλλιώς μορφοποίησε το αλφαριθμητικό και σιωπηρά αφαίρεσε τα ανεπιθύμητα \ αν είμαστε σε ExpertMode
        } else txtName.setText(txtName.getText().replace("\\","").trim());
            // ενδεχόμενος περιορισμός αλφαριθμητικού πεδίου στο μέγεθος που έχει δηλωθεί στο αντίστοιχο POJO
            if(txtName.getText().length() > rss.fieldLength(this, PlayList.class, "name")) {
                txtName.setText(txtName.getText().substring(0, rss.fieldLength(this, PlayList.class, "name"))); }
            tempTxt = txtName.getText();  // μετά τις μορφοποιήσεις επικαιροποίησε την πρότερη κατάσταση
            // αν η κλήση της μεθόδου δεν προέρχεται από κάποιο focus lost, highlighted επιλογή πεδίου
            if(evt != null) { System.out.println("   ~selectAll"); txtName.selectAll(); }
    }//GEN-LAST:event_txtNameActionPerformed

    private void jButtonEXPORTActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonEXPORTActionPerformed
        ei.SaveXML(this);  // Εξαγωγή των τραγουδιών στο αρχείο file.xml
    }//GEN-LAST:event_jButtonEXPORTActionPerformed

    private void jButtonIMPORTActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonIMPORTActionPerformed
        ei.ImportXML(this);  // Εισαγωγή των τραγουδιών από αρχείο file.xml
    }//GEN-LAST:event_jButtonIMPORTActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JTable PlayListTABLE;
    private javax.swing.JTable SongsTABLE;
    private javax.swing.JButton jButtonADD;
    private javax.swing.JButton jButtonADDSONG;
    private javax.swing.JButton jButtonCANCEL;
    private javax.swing.JButton jButtonEDIT;
    private javax.swing.JButton jButtonEXPORT;
    private javax.swing.JButton jButtonIMPORT;
    private javax.swing.JButton jButtonREMOVE;
    private javax.swing.JButton jButtonREMOVESONG;
    private javax.swing.JButton jButtonRETURN;
    private javax.swing.JButton jButtonSAVE;
    private javax.swing.JLabel jLabelBACKGROUND;
    private javax.swing.JLabel jLabelFORM;
    private javax.swing.JLabel jLabelMAINTABLE;
    private javax.swing.JLabel jLabelNANOPLAYER;
    private javax.swing.JLabel jLabelNOTES;
    private javax.swing.JLabel jLabelRSSLOGO;
    private javax.swing.JLabel jLabelRSTEAM;
    private javax.swing.JPanel jPanelFORM;
    private javax.swing.JScrollPane jScrollPaneFORM;
    private javax.swing.JScrollPane jScrollPaneMAINTABLE;
    private javax.swing.JSeparator jSeparatorLINE;
    private java.util.List<PlayList> listPlayList;
    private java.util.List<Song> listSongsInPlayList;
    private javax.persistence.Query queryPlayList;
    private javax.persistence.Query querySongsInPlayList;
    private javax.swing.JTextField txtCreationDate;
    public javax.swing.JTextField txtDuration;
    private javax.swing.JTextField txtName;
    private org.jdesktop.beansbinding.BindingGroup bindingGroup;
    // End of variables declaration//GEN-END:variables
}